package interceptor

import (
	"context"
	"errors"
	"log"
	"protobuf-learn/proto"

	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/metadata"
	"google.golang.org/grpc/status"
)

const userIdCtxKey = "user_id"

type Validator interface {
	ValidateToken(ctx context.Context, token string) (string, error)
}

type middleware struct {
	validator Validator
}

func NewMiddleware(validator Validator) (*middleware, error) {
	if validator == nil {
		return nil, errors.New("validator cannot be nil")
	}

	return &middleware{
		validator: validator,
	}, nil
}

func (m *middleware) UnaryAuthMiddleware(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp any, err error) {
	// check the RPC method and only run if its the Proected RPC
	if info.FullMethod != proto.InterceptorService_Protected_FullMethodName {
		return handler(ctx, req)
	}
	log.Println("UnaryAuthMiddleware-------")
	// get the token from the metadata
	token, err := getTokenFromMetadata(ctx)
	if err != nil {
		return nil, status.Error(codes.PermissionDenied, "token must be provied")
	}
	// call validate token
	userId, err := m.validator.ValidateToken(ctx, token)
	if err != nil {
		return nil, status.Error(codes.PermissionDenied, "invalid token==>"+err.Error())
	}
	// add the user ID to hte context
	ctx = context.WithValue(ctx, userIdCtxKey, userId)

	// call our handller
	return handler(ctx, req)
}

func getTokenFromMetadata(ctx context.Context) (string, error) {
	md, ok := metadata.FromIncomingContext(ctx)
	if !ok || len(md["authorization"]) != 1 {
		return "", errors.New("token not found in metadata")
	}

	return md["authorization"][0], nil
}
